home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / HOP.C < prev    next >
C/C++ Source or Header  |  1997-08-18  |  11KB  |  412 lines

  1. /*
  2.  *    HOP.C   -- trace route packets take to a remote host
  3.  *
  4.  *    02-90    -- Katie Stevens (dkstevens@ucdavis.edu)
  5.  *           UC Davis, Computing Services
  6.  *           Davis, CA
  7.  *    04-90    -- Modified by Phil Karn to use raw IP sockets to read replies
  8.  *    08-90    -- Modified by Bill Simpson to display domain names
  9.  *
  10.  *  Mods by PA0GRI  (newsession param)
  11.  */
  12.  
  13. #include "global.h"
  14. #include "commands.h"
  15. #include "mbuf.h"
  16. #include "usock.h"
  17. #include "session.h"
  18. #include "domain.h"
  19. #include "icmp.h"
  20.  
  21. #if !defined(_lint)
  22. static char rcsid[] OPTIONAL = "$Id: hop.c,v 1.20 1997/08/19 01:19:22 root Exp root $";
  23. #endif
  24.  
  25. #define HOPMAXQUERY    5        /* Max# queries each TTL value */
  26. static int16 Hoprport = 32768 + 666;    /* funny port for udp probes */
  27.  
  28. #if 0    /* not used */
  29. #define HOP_HIGHBIT    32768        /* Mask to check ICMP msgs */
  30. #endif
  31.  
  32.  
  33. #define HOPTRACE    1        /* Enable HOP tracing */
  34. #ifdef HOPTRACE
  35. static int Hoptrace = 0;
  36. static int hoptrace (int argc, char *argv[], void *p);
  37. #endif
  38.  
  39.  
  40. static unsigned short Hopmaxttl = 30;    /* max attempts */
  41. static unsigned short Hopmaxwait = 5;    /* secs timeout each attempt */
  42. static unsigned short Hopquery = 3;    /* #probes each attempt */
  43.  
  44. static int hopcheck (int argc, char *argv[], void *p);
  45. static int hopttl (int argc, char *argv[], void *p);
  46. static int hopwait (int argc, char *argv[], void *p);
  47. static int hopnum (int argc, char *argv[], void *p);
  48. static int geticmp (int s, int16 lport, int16 fport,
  49.                 uint32 * sender, char *type, char *code);
  50.  
  51. static struct cmds Hopcmds[] =
  52. {
  53.     { "check",    hopcheck,    2048, 2, "check [-n] <host>" },
  54.     { "maxttl",    hopttl,        0, 0, NULLCHAR },
  55.     { "maxwait",    hopwait,    0, 0, NULLCHAR },
  56.     { "queries",    hopnum,        0, 0, NULLCHAR },
  57. #ifdef HOPTRACE
  58.     { "trace",    hoptrace,    0, 0, NULLCHAR },
  59. #endif
  60.     { NULLCHAR,    0,        0, 0, NULLCHAR }
  61. };
  62.  
  63.  
  64. /* attempt to trace route to a remote host */
  65. int
  66. dohop (int argc, char *argv[], void *p)
  67. {
  68.     return subcmd (Hopcmds, argc, argv, p);
  69. }
  70.  
  71.  
  72. /* Set/show # queries sent each TTL value */
  73. static int
  74. hopnum (int argc, char *argv[], void *p OPTIONAL)
  75. {
  76. int r;
  77. int16 x = Hopquery;
  78.  
  79.     r = setshort (&x, "# queries each attempt", argc, argv);
  80.     if ((((short) x) <= 0) || (x > HOPMAXQUERY)) {
  81.         tprintf ("Must be  0 < x <= %d\n", HOPMAXQUERY);
  82.         return 0;
  83.     } else
  84.         Hopquery = x;
  85.  
  86.     return r;
  87. }
  88.  
  89.  
  90. #ifdef HOPTRACE
  91. /* Set/show tracelevel */
  92. static int
  93. hoptrace (int argc, char *argv[], void *p OPTIONAL)
  94. {
  95.     return setbool (&Hoptrace, "HOPCHECK tracing", argc, argv);
  96. }
  97. #endif
  98.  
  99.  
  100. /* Set/show maximum TTL value for a hopcheck query */
  101. static int
  102. hopttl (int argc, char *argv[], void *p OPTIONAL)
  103. {
  104. int r;
  105. int16 x = Hopmaxttl;
  106.  
  107.     r = setshort (&x, "Max attempts to reach host", argc, argv);
  108.     if ((((short) x) <= 0) || (x > 255)) {
  109.         tputs ("Must be  0 < x <= 255\n");
  110.         return 0;
  111.     } else
  112.         Hopmaxttl = x;
  113.  
  114.     return r;
  115. }
  116.  
  117.  
  118. /* Set/show #secs until timeout for a hopcheck query */
  119. static int
  120. hopwait (int argc, char *argv[], void *p OPTIONAL)
  121. {
  122. int r;
  123. int16 x = Hopmaxwait;
  124.  
  125.     r = setshort (&x, "# secs to wait for reply to query", argc, argv);
  126.     if (((short) x) <= 0) {
  127.         tputs ("Must be >= 0\n");
  128.         return 0;
  129.     } else
  130.         Hopmaxwait = x;
  131.  
  132.     return r;
  133. }
  134.  
  135.  
  136. /* send probes to trace route of a remote host */
  137. static int
  138. hopcheck (int argc, char *argv[], void *p OPTIONAL)
  139. {
  140. struct session *sp;    /* Session for trace output */
  141. int s;            /* Socket for queries */
  142. int s1;            /* Raw socket for replies */
  143. struct socket lsocket;    /* Local socket sending queries */
  144. struct socket rsocket;    /* Final destination of queries */
  145. int32 cticks;        /* Timer for query replies */
  146. uint32 icsource = 0;    /* Sender of last ICMP reply */
  147. char ictype = 0;    /* ICMP type last ICMP reply */
  148. char iccode = 0;    /* ICMP code last ICMP reply */
  149. uint32 lastaddr;    /* Sender of previous ICMP reply */
  150. struct sockaddr_in sock;
  151. register struct usock *usp;
  152. register struct sockaddr_in *sinp;
  153. unsigned char sndttl, q;
  154. int tracedone = 0;
  155. int ilookup = 1;    /* Control of inverse domain lookup */
  156. char *hostname;
  157. int trans;        /* Save IP address translation state */
  158. int save_trace;
  159. int useind = 1;
  160. #ifdef g1emmx
  161. char *sname;        /* Used in resolve_a() call */
  162. #endif
  163.         
  164.     /*Make sure this comes from console - WG7J*/
  165.     if (Curproc->input != Command->input)
  166.         return 0;
  167.  
  168.     if (!strcasecmp (argv[1], "-n"))    {
  169.         ilookup = 0;
  170.         useind = 2;
  171.         if (argc < 3)    {
  172.             tprintf ("Usage: hop check [-n] <host>");
  173.             return 1;
  174.         }
  175.     }
  176.         
  177.     hostname = argv[useind];
  178.     /* Allocate a session descriptor */
  179.     if ((sp = newsession (hostname, HOP, 0)) == NULLSESSION) {
  180.         tputs (TooManySessions);
  181.         (void) keywait (NULLCHAR, 1);
  182.         return 1;
  183.     }
  184.     sp->s = s = -1;
  185.     sp->flowmode = 1;
  186.  
  187.     /* Setup UDP socket to remote host */
  188.     sock.sin_family = AF_INET;
  189.     sock.sin_port = Hoprport;
  190.     tprintf ("Resolving %s... ", hostname);
  191.     if ((sock.sin_addr.s_addr = resolve (hostname)) == 0) {
  192.         tprintf (Badhost, hostname);
  193.         (void) keywait (NULLCHAR, 1);
  194.         freesession (sp);
  195.         return 1;
  196.     }
  197.     /* Open socket to remote host */
  198.     tprintf ("hopcheck to %s\n", psocket ((void *) &sock));
  199.     if ((sp->s = s = socket (AF_INET, SOCK_DGRAM, 0)) == -1) {
  200.         tputs (Nosock);
  201.         (void) keywait (NULLCHAR, 1);
  202.         freesession (sp);
  203.         return 1;
  204.     }
  205.     if (connect (s, (char *) &sock, sizeof (sock)) == -1) {
  206.         tputs ("Connect failed\n");
  207.         (void) keywait (NULLCHAR, 1);
  208.         freesession (sp);
  209.         return 1;
  210.     }
  211.     if ((s1 = socket (AF_INET, SOCK_RAW, ICMP_PTCL)) == -1) {
  212.         tputs (Nosock);
  213.         (void) keywait (NULLCHAR, 1);
  214.         freesession (sp);
  215.         return 1;
  216.     }
  217.     /* turn off icmp tracing while hop-checking */
  218.     save_trace = Icmp_trace;
  219.     Icmp_trace = 0;
  220.     /* Setup structures to send queries */
  221.     /* Retrieve socket details for user socket control block */
  222.     usp = itop (s);
  223.     sinp = (struct sockaddr_in *) usp->name;
  224.     lsocket.address = sinp->sin_addr.s_addr;
  225.     lsocket.port = sinp->sin_port;
  226.     sinp = (struct sockaddr_in *) usp->peername;
  227.     rsocket.address = sinp->sin_addr.s_addr;
  228.  
  229.     /* Send queries with increasing TTL; start with TTL=1 */
  230.     if (Hoptrace)
  231.         log (sp->s, "HOPCHECK start trace to %s\n", sp->name);
  232.     for (sndttl = 1; (sndttl < Hopmaxttl); ++sndttl, sinp->sin_port++) {
  233.         /* Increment funny UDP port number each round */
  234.         rsocket.port = sinp->sin_port;
  235.         tprintf ("%3d:", sndttl);
  236.         lastaddr = (int32) 0;
  237.         /* Send a round of queries */
  238.         for (q = 0; (q < Hopquery); ++q) {
  239.             (void) send_udp (&lsocket, &rsocket, 0, (char) sndttl, NULLBUF, 0, 0, 0);
  240.             cticks = msclock ();
  241.             kalarm (((long) Hopmaxwait) * 1000L);
  242.  
  243.             /* Wait for a reply to our query */
  244.             if (geticmp (s1, lsocket.port, rsocket.port, &icsource, &ictype, &iccode) == -1) {
  245.                 if (errno != EALARM) {
  246.                     kalarm (0L);    /* cancel alarm */
  247.                     goto done;    /* User reset */
  248.                 }
  249.                 /* Alarm rang, give up waiting for replies */
  250.                 tputs (" ***");
  251.                 continue;
  252.             }
  253.             /* Save #ticks taken for reply */
  254.             cticks = msclock () - cticks;
  255.             /* Report ICMP reply */
  256.             if (icsource != lastaddr) {
  257.                 struct rr *save_rrlp, *rrlp;
  258.  
  259.                 if (lastaddr != (int32) 0)
  260.                     tputs ("\n    ");
  261.                 /* Save IP address translation state */
  262.                 trans = DTranslate;
  263.                 /* Force output to be numeric IP addr */
  264.                 DTranslate = 0;
  265.                 tprintf (" %-15s", inet_ntoa (icsource));
  266.                 /* Restore original state */
  267.                 DTranslate = trans;
  268. #ifdef g1emmx
  269.                 if ((sname = resolve_a (icsource, FALSE)) != NULLCHAR) {
  270.                     tprintf (" %s", sname);
  271.                     free (sname);
  272.                 }
  273. #else
  274.                 if (ilookup) {
  275.                     for (rrlp = save_rrlp = inverse_a (icsource);
  276.                          rrlp != NULLRR;
  277.                          rrlp = rrlp->next) {
  278.                         if (rrlp->rdlength > 0) {
  279.                             switch (rrlp->type) {
  280.                                 case TYPE_PTR:
  281.                                     tprintf (" %s", rrlp->rdata.name);
  282.                                     break;
  283.                                 case TYPE_A:
  284.                                     tprintf (" %s", rrlp->name);
  285.                                     break;
  286.                                 default:
  287.                                     break;
  288.                             }
  289.                             if (rrlp->next != NULLRR)
  290.                                 tprintf ("\n%20s", " ");
  291.                         }
  292.                     }
  293.                     free_rr (save_rrlp);
  294. #endif
  295.                 }
  296.                 lastaddr = icsource;
  297.             }
  298.             tprintf (" (%ld ms)", cticks);
  299. #ifdef HOPTRACE
  300.             if (Hoptrace)
  301.                 log (sp->s, "(hopcheck) ICMP from %s (%ldms) %s %s",
  302.                      inet_ntoa (icsource), cticks, Icmptypes[(int) ictype],
  303.                      ((ictype == ICMP_TIME_EXCEED) ? Exceed[(int) iccode] : Unreach[(int) iccode]));
  304. #endif
  305.  
  306.             /* Check type of reply */
  307.             if (ictype == ICMP_TIME_EXCEED)
  308.                 continue;
  309.             /* Reply was: destination unreachable */
  310.             switch (iccode) {
  311.                 case ICMP_PORT_UNREACH:        ++tracedone;
  312.                                 break;
  313.                 case ICMP_NET_UNREACH:        ++tracedone;
  314.                                 tputs (" !N");
  315.                                 break;
  316.                 case ICMP_HOST_UNREACH:        ++tracedone;
  317.                                 tputs (" !H");
  318.                                 break;
  319.                 case ICMP_PROT_UNREACH:        ++tracedone;
  320.                                 tputs (" !P");
  321.                                 break;
  322.                 case ICMP_FRAG_NEEDED:        ++tracedone;
  323.                                 tputs (" !F");
  324.                                 break;
  325.                 case ICMP_ROUTE_FAIL:        ++tracedone;
  326.                                 tputs (" !S");
  327.                                 break;
  328.                 case ICMP_ADMIN_PROHIB:        ++tracedone;
  329.                                 tputs (" !A");
  330.                                 break;
  331.                 default:            tputs (" !?");
  332.                                 break;
  333.             }
  334.         }
  335.         /* Done with this round of queries */
  336.         kalarm (0L);
  337.         tputc ('\n');
  338.         /* Check if we reached remote host this round */
  339.         if (tracedone != 0)
  340.             break;
  341.     }
  342.  
  343.     /* Done with hopcheck */
  344. done:    close_s (s);
  345.     sp->s = -1;
  346.     close_s (s1);
  347.     tputs ("hopcheck done: ");
  348.     Icmp_trace = save_trace;
  349.     if (sndttl >= Hopmaxttl)
  350.         tputs ("!! maximum TTL exceeded\n");
  351.     else if ((icsource == rsocket.address) && (iccode == ICMP_PORT_UNREACH))
  352.         tprintf ("normal (%s %s)\n", Icmptypes[(int) ictype], Unreach[(int) iccode]);
  353.     else
  354.         tprintf ("!! %s %s\n", Icmptypes[(int) ictype], Unreach[(int) iccode]);
  355.  
  356. #ifdef HOPTRACE
  357.     if (Hoptrace)
  358.         log (sp->s, "HOPCHECK to %s done", sp->name);
  359. #endif
  360.     (void) keywait (NULLCHAR, 1);
  361.     freesession (sp);
  362.     return 0;
  363. }
  364.  
  365.  
  366. /* Read raw network socket looking for ICMP messages in response to our
  367.  * UDP probes
  368.  */
  369. static int
  370. geticmp (int s, int16 lport, int16 fport, uint32 *sender, char *type, char *code)
  371. {
  372. int size;
  373. struct icmp icmphdr;
  374. struct ip iphdr;
  375. struct udp udphdr;
  376. struct mbuf *bp;
  377. struct sockaddr_in sock;
  378.  
  379.     for ( ; ; ) {
  380.         size = sizeof (sock);
  381.         if (recv_mbuf (s, &bp, 0, (char *) &sock, &size) == -1)
  382.             return -1;
  383.         /* It's an ICMP message, let's see if it's interesting */
  384.         (void) ntohicmp (&icmphdr, &bp);
  385.         if ((icmphdr.type != ICMP_TIME_EXCEED ||
  386.              icmphdr.code != ICMP_TTL_EXCEED)
  387.             && icmphdr.type != ICMP_DEST_UNREACH) {
  388.             /* We're not interested in these */
  389.             free_p (bp);
  390.             continue;
  391.         }
  392.         (void) ntohip (&iphdr, &bp);
  393.         if (iphdr.protocol != UDP_PTCL) {
  394.             /* Not UDP, so can't be interesting */
  395.             free_p (bp);
  396.             continue;
  397.         }
  398.         (void) ntohudp (&udphdr, &bp);
  399.         if (udphdr.dest != fport || udphdr.source != lport) {
  400.             /* Not from our hopcheck session */
  401.             free_p (bp);
  402.             continue;
  403.         }
  404.         /* Passed all of our checks, so return it */
  405.         *sender = sock.sin_addr.s_addr;
  406.         *type = icmphdr.type;
  407.         *code = icmphdr.code;
  408.         free_p (bp);
  409.         return 0;
  410.     }
  411. }
  412.